#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>

#include "librc.h"

// TO DO: redo serial port handling

static int set_interface_attribs(int fd, int speed, int parity)
{
	struct termios tty;
	memset(&tty, 0, sizeof tty);
	if (tcgetattr(fd, &tty) != 0) {
		fprintf(stderr, "error %d from tcgetattr", errno);
		return -1;
	}

	cfsetospeed (&tty, speed);
	cfsetispeed (&tty, speed);

	tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
	// disable IGNBRK for mismatched speed tests; otherwise receive break
	// as \000 chars
	tty.c_iflag &= ~IGNBRK;	 // ignore break signal
	tty.c_lflag = 0;		// no signaling chars, no echo,
					// no canonical processing
	tty.c_oflag = 0;		// no remapping, no delays
	tty.c_cc[VMIN]  = 0;	    // read doesn't block
	tty.c_cc[VTIME] = 5;	    // 0.5 seconds read timeout

	tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

	tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
					// enable reading
	tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
	tty.c_cflag |= parity;
	tty.c_cflag &= ~CSTOPB;
	tty.c_cflag &= ~CRTSCTS;

	if (tcsetattr(fd, TCSANOW, &tty) != 0) {
		fprintf(stderr, "error %d from tcsetattr", errno);
		return -1;
	}
	return 0;
}

static void set_blocking(int fd, int should_block)
{
	struct termios tty;
	memset (&tty, 0, sizeof tty);
	if (tcgetattr(fd, &tty) != 0) {
		fprintf(stderr, "error %d from tggetattr", errno);
		return;
	}

	tty.c_cc[VMIN]  = should_block ? 1 : 0;
	tty.c_cc[VTIME] = 5;	    // 0.5 seconds read timeout

	if (tcsetattr(fd, TCSANOW, &tty) != 0)
		fprintf(stderr, "error %d setting term attributes", errno);
}

/*
conditions = {
{"OK", 0},
{"ERROR", 1},
{"COMMAND NOT SUPPORT", 1},
{"TOO MANY PARAMETERS", 1},
{"+CME ERROR: ", 1},
{}
};
*/

int atcommand(int fd, const char *cmd, char *res, int ressz, int filter)
{
	int ret = 1;

	char cmdbuf[256];
	snprintf(cmdbuf, sizeof(cmdbuf), "%s\r\n", cmd);
	//printf("-> %s\n", cmd);

	write(fd, cmdbuf, strlen(cmdbuf));

	if (res && ressz)
		memset(res, 0, ressz);

	char match[64];
	if (res && ressz && filter && sscanf(cmd, "AT%63[^=?\r\n]", match) != 1)
		return 1;
	snprintf(cmdbuf, sizeof(cmdbuf), "%s:", match);

	int start = 0, end = 0;

	int timeout = 2000;

	if (!strcmp(cmd, "AT+COPS=?"))
		timeout = 90000;

	int tfd = start_timer(timeout);
	
	if (tfd < 0)
		return 1;

	int bufsz = 4096;
	char *buf = malloc(bufsz);

	while (1) {
	
		//sleep(1);
		memset(buf, 0, bufsz);
		read(fd, buf, bufsz - 1);

		//printf("<debug>\n%s\n</debug>\n", buf);

		char *tmp = buf, *line;
		//for (line = strsep(&tmp, "\n"); line; line = strsep(&tmp, "\n")) {
		//char *tmp, *line;
		//for (tmp = buf; (line = strsep(&tmp, "\n"));) {
		while ((line = strsep(&tmp, "\n"))) {
			if (!*line)
				continue;
			if (start == 0 && !strcmp(line, cmd))
				start = 1;
			else if (start == 1) {
				if (!strcmp(line, "OK") || !strcmp(line, "ERROR") || !strcmp(line, "COMMAND NOT SUPPORT") ||
				    !strcmp(line, "TOO MANY PARAMETERS") || !strncmp(line, "+CME ERROR: ", 12)) {
					end = 1;
					break;
				}
				if (res && ressz && (!filter || !strncmp(line, cmdbuf, strlen(cmdbuf))))
					snprintfcat(res, ressz, "%s\n", line);
			}
		}

		if (end == 1 && line && *line) {
			if (!strcmp(line, "ERROR") || !strcmp(line, "COMMAND NOT SUPPORT") ||
			    !strcmp(line, "TOO MANY PARAMETERS") || !strncmp(line, "+CME ERROR: ", 12)) {
				if (res && ressz)
					snprintfcat(res, ressz, "%s\n", line);
				ret = 2;
			}
			if (!strcmp(line, "OK")) {
				// put OK instead of empty string
				// for commands without result string, e.g. AT
				if (res && ressz && !*res)
					snprintfcat(res, ressz, "%s\n", line);
				ret = 0;
			}
			break;
		}

		if (check_timer(tfd)) {
			ret = 3;
			break;
		}

		//usleep(100000);
	}

	close_timer(tfd);
	free(buf);

	return ret;
}

int port_open(char *portname)
{
	int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
	if (fd < 0)
		return fd;
	set_interface_attribs(fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
	set_blocking(fd, 0);           // set no blocking
	usleep(100000);
	tcflush(fd, TCIFLUSH); // clear buffer
	return fd;
}
